/*
 * Copyright (c) 2007 Apple Inc.  All Rights Reserved.
 * 
 * @APPLE_LICENSE_HEADER_START@
 * 
 * This file contains Original Code and/or Modifications of Original Code
 * as defined in and that are subject to the Apple Public Source License
 * Version 2.0 (the 'License'). You may not use this file except in
 * compliance with the License. Please obtain a copy of the License at
 * http://www.opensource.apple.com/apsl/ and read it before using this
 * file.
 * 
 * The Original Code and all software distributed under the License are
 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
 * Please see the License for the specific language governing rights and
 * limitations under the License.
 * 
 * @APPLE_LICENSE_HEADER_END@
 */

/***********************************************************************
* objc-os.m
* OS portability layer.
**********************************************************************/

#include "objc-private.h"
#include "objc-loadmethod.h"
#include "objc-bp-assist.h"

#if TARGET_OS_WIN32

#include "objc-runtime-old.h"
#include "objcrt.h"

const fork_unsafe_lock_t fork_unsafe_lock;

int monitor_init(monitor_t *c) 
{
    // fixme error checking
    HANDLE mutex = CreateMutex(NULL, TRUE, NULL);
    while (!c->mutex) {
        // fixme memory barrier here?
        if (0 == InterlockedCompareExchangePointer(&c->mutex, mutex, 0)) {
            // we win - finish construction
            c->waiters = CreateSemaphore(NULL, 0, 0x7fffffff, NULL);
            c->waitersDone = CreateEvent(NULL, FALSE, FALSE, NULL);
            InitializeCriticalSection(&c->waitCountLock);
            c->waitCount = 0;
            c->didBroadcast = 0;
            ReleaseMutex(c->mutex);    
            return 0;
        }
    }

    // someone else allocated the mutex and constructed the monitor
    ReleaseMutex(mutex);
    CloseHandle(mutex);
    return 0;
}

void mutex_init(mutex_t *m)
{
    while (!m->lock) {
        CRITICAL_SECTION *newlock = malloc(sizeof(CRITICAL_SECTION));
        InitializeCriticalSection(newlock);
        // fixme memory barrier here?
        if (0 == InterlockedCompareExchangePointer(&m->lock, newlock, 0)) {
            return;
        }
        // someone else installed their lock first
        DeleteCriticalSection(newlock);
        free(newlock);
    }
}


void recursive_mutex_init(recursive_mutex_t *m)
{
    // fixme error checking
    HANDLE newmutex = CreateMutex(NULL, FALSE, NULL);
    while (!m->mutex) {
        // fixme memory barrier here?
        if (0 == InterlockedCompareExchangePointer(&m->mutex, newmutex, 0)) {
            // we win
            return;
        }
    }
    
    // someone else installed their lock first
    CloseHandle(newmutex);
}


WINBOOL APIENTRY DllMain( HMODULE hModule,
                       DWORD  ul_reason_for_call,
                       LPVOID lpReserved
					 )
{
    switch (ul_reason_for_call) {
    case DLL_PROCESS_ATTACH:
        environ_init();
        tls_init();
        runtime_init();
        sel_init(3500);  // old selector heuristic
        exception_init();
        break;

    case DLL_THREAD_ATTACH:
        break;

    case DLL_THREAD_DETACH:
    case DLL_PROCESS_DETACH:
        break;
    }
    return TRUE;
}

OBJC_EXPORT void *_objc_init_image(HMODULE image, const objc_sections *sects)
{
    header_info *hi = malloc(sizeof(header_info));
    size_t count, i;

    hi->mhdr = (const headerType *)image;
    hi->info = sects->iiStart;
    hi->allClassesRealized = NO;
    hi->modules = sects->modStart ? (Module *)((void **)sects->modStart+1) : 0;
    hi->moduleCount = (Module *)sects->modEnd - hi->modules;
    hi->protocols = sects->protoStart ? (struct old_protocol **)((void **)sects->protoStart+1) : 0;
    hi->protocolCount = (struct old_protocol **)sects->protoEnd - hi->protocols;
    hi->imageinfo = NULL;
    hi->imageinfoBytes = 0;
    // hi->imageinfo = sects->iiStart ? (uint8_t *)((void **)sects->iiStart+1) : 0;;
//     hi->imageinfoBytes = (uint8_t *)sects->iiEnd - hi->imageinfo;
    hi->selrefs = sects->selrefsStart ? (SEL *)((void **)sects->selrefsStart+1) : 0;
    hi->selrefCount = (SEL *)sects->selrefsEnd - hi->selrefs;
    hi->clsrefs = sects->clsrefsStart ? (Class *)((void **)sects->clsrefsStart+1) : 0;
    hi->clsrefCount = (Class *)sects->clsrefsEnd - hi->clsrefs;

    count = 0;
    for (i = 0; i < hi->moduleCount; i++) {
        if (hi->modules[i]) count++;
    }
    hi->mod_count = 0;
    hi->mod_ptr = 0;
    if (count > 0) {
        hi->mod_ptr = malloc(count * sizeof(struct objc_module));
        for (i = 0; i < hi->moduleCount; i++) {
            if (hi->modules[i]) memcpy(&hi->mod_ptr[hi->mod_count++], hi->modules[i], sizeof(struct objc_module));
        }
    }
    
    hi->moduleName = malloc(MAX_PATH * sizeof(TCHAR));
    GetModuleFileName((HMODULE)(hi->mhdr), hi->moduleName, MAX_PATH * sizeof(TCHAR));

    appendHeader(hi);

    if (PrintImages) {
        _objc_inform("IMAGES: loading image for %s%s%s%s\n", 
                     hi->fname, 
                     headerIsBundle(hi) ? " (bundle)" : "", 
                     hi->info->isReplacement() ? " (replacement)":"", 
                     hi->info->hasCategoryClassProperties() ? " (has class properties)":"");
    }

    // Count classes. Size various table based on the total.
    int total = 0;
    int unoptimizedTotal = 0;
    {
      if (_getObjc2ClassList(hi, &count)) {
        total += (int)count;
        if (!hi->getInSharedCache()) unoptimizedTotal += count;
      }
    }

    _read_images(&hi, 1, total, unoptimizedTotal);

    return hi;
}

// 1: 把mh中实现了load方法的load添加到全局数组：loadable_classes 中
// 2: 把mh中实现了load方法的cat添加到全局数组：loadable_categories 中
// 3: 调用所有的load方法
OBJC_EXPORT void _objc_load_image(HMODULE image, header_info *hinfo) {
    // 1: 把mh中实现了load方法的load添加到全局数组：loadable_classes 中
    // 2: 把mh中实现了load方法的cat添加到全局数组：loadable_categories 中
    prepare_load_methods(hinfo);
    // 调用所有的load方法
    call_load_methods();
}

OBJC_EXPORT void _objc_unload_image(HMODULE image, header_info *hinfo)
{
    _objc_fatal("image unload not supported");
}


// TARGET_OS_WIN32
#elif TARGET_OS_MAC

#include "objc-file-old.h"
#include "objc-file.h"


/***********************************************************************
* libobjc must never run static destructors. 
* Cover libc's __cxa_atexit with our own definition that runs nothing.
* rdar://21734598  ER: Compiler option to suppress C++ static destructors
**********************************************************************/
extern "C" int __cxa_atexit();
extern "C" int __cxa_atexit() { return 0; }


/***********************************************************************
* bad_magic. 校验mhdr：通过校验macho.magic是否是：MH_MAGIC、MH_MAGIC_64、MH_CIGAM、MH_CIGAM_64中的任何一个
* Return YES if the header has invalid Mach-o magic.
**********************************************************************/
bool bad_magic(const headerType *mhdr) {
    return (mhdr->magic != MH_MAGIC  &&  mhdr->magic != MH_MAGIC_64  &&  
            mhdr->magic != MH_CIGAM  &&  mhdr->magic != MH_CIGAM_64);
}

/* 加载mhdr
 * 1: 校验mhdr是否正确：校验mhdr的heade.magic是否正确
 * 2: mhdr是共享缓存中的库，说明已经完成了加载，可以直接拿到header_info，设置load状态：已加载
 * 3: mhdr不是共享缓存中的库:
 *   3.1: 校验mdhr：只要mhdr中有(__DATA, __objc_imageinfo) 或者 (__OBJC)任何一个都行
 *   3.2: 使用calloc申请内存空间创建header_info
 *   3.3: 初始化header_info：设置mhdr、image_info、load(状态)=true、allClassesRealized(标识位)= NO
 * 4: 统计Image中类个数：
 *   4.1: totalClasses: 所有Image中类的总个数
 *   4.2: unoptimizedTotalClasses:不在共享缓存中Image的类的总个数
 * 5: 把所有的header_info放到全局链表FirstHeader中、
 * 6: 把不在共享缓存的hi所有segment得位置存到到全局数组dataSegmentsRanges.ranges中(在add数据时，自动扩容)
 */
static header_info * addHeader(const headerType *mhdr, const char *path, int &totalClasses, int &unoptimizedTotalClasses) {
    header_info *hi;

    // 校验mhdr：通过校验macho.magic是否是：MH_MAGIC、MH_MAGIC_64、MH_CIGAM、MH_CIGAM_64中的任何一个
    if (bad_magic(mhdr)) return NULL;

    bool inSharedCache = false;

    // 在共享缓存中查询mach-o
    // Look for hinfo from the dyld shared cache.
    hi = preoptimizedHinfoForHeader(mhdr);
    if (hi) {   // mhdr在共享缓存中
        // Found an hinfo in the dyld shared cache.

        // Weed out duplicates.
        if (hi->isLoaded()) {       // 如果mhdr已经完成了load，返回NULL
            return NULL;
        }

        // 修改标记为：在共享缓存中
        inSharedCache = true;

        // Initialize fields not set by the shared cache
        // hi->next is set by appendHeader
        hi->setLoaded(true);        // 把load状态修改为：已加载

        if (PrintPreopt) {
            _objc_inform("PREOPTIMIZATION: honoring preoptimized header info at %p for %s", hi, hi->fname());
        }

#if !__OBJC2__
        _objc_fatal("shouldn't be here");
#endif
#if DEBUG
        // Verify image_info
        size_t info_size = 0;
        // 获取mach-o文件中Section(__DATA, __objc_imageinfo)的信息
        const objc_image_info *image_info = _getObjcImageInfo(mhdr,&info_size);
        ASSERT(image_info == hi->info());
#endif
    } else  {       // mhdr不在共享缓存中
        // Didn't find an hinfo in the dyld shared cache.

        // 1: 校验mhdr：只要有(__DATA, __objc_imageinfo) 或者 (__OBJC)任何一个都行
        // Locate the __OBJC segment
        size_t info_size = 0;
        unsigned long seg_size;
        // 获取mach-o文件中Section(__DATA, __objc_imageinfo)的信息
        const objc_image_info *image_info = _getObjcImageInfo(mhdr,&info_size);
        // 获取mach-o文件中所有SEG_OBJC的Section信息      #define SEG_OBJC "__OBJC" /* Objective-C runtime 段 */
        const uint8_t *objc_segment = getsegmentdata(mhdr,SEG_OBJC,&seg_size);
        if (!objc_segment  &&  !image_info) return NULL;

        // 2: calloc创建一个header_info
        /*
         |--------------------------------------------
         |  header_info  |  header_info_rw  | 1 |
         |--------------------------------------------
         */
        // Allocate a header_info entry.
        // Note we also allocate space for a single header_info_rw in the
        // rw_data[] inside header_info.
        hi = (header_info *)calloc(sizeof(header_info) + sizeof(header_info_rw), 1);

        // 3: 初始化header_info
        // 把mhdr保存到header_info中
        // Set up the new header_info entry.
        hi->setmhdr(mhdr);
#if !__OBJC2__
        // mhdr must already be set
        hi->mod_count = 0;
        hi->mod_ptr = _getObjcModules(hi, &hi->mod_count);
#endif
        // 把mach-o中的Section(__DATA, __objc_imageinfo)的信息保存到header_info中
        // Install a placeholder image_info if absent to simplify code elsewhere
        static const objc_image_info emptyInfo = {0, 0};
        hi->setinfo(image_info ?: &emptyInfo);

        // 设置标识位load（是否完成load）：已加载
        hi->setLoaded(true);
        // 设置标识位allClassesRealized（是否所有的类都完成realized）：未完成
        hi->setAllClassesRealized(NO);
    }

#if __OBJC2__
    {
        
        // 4: 统计Image中类个数：
        //   4.1: totalClasses: 所有Image中类的总个数
        //   4.2: unoptimizedTotalClasses:不在共享缓存中Image的类的总个数
        size_t count = 0;
        // 读取hi里所有的类的个数
        if (_getObjc2ClassList(hi, &count)) {
            // 统计类的总个数
            totalClasses += (int)count;
            // 统计‘不在共享缓存’类的个数
            if (!inSharedCache) unoptimizedTotalClasses += count;
        }
    }
#endif

    /*
     * 1: 把hi放到全局链表中
     * 2: 把不在共享缓存的hi所有segment得位置存到到全局数组dataSegmentsRanges.ranges中(在add数据时，自动扩容)
     */
    appendHeader(hi);
    return hi;
}


/***********************************************************************
* linksToLibrary
* Returns true if the image links directly to a dylib whose install name 
* is exactly the given name.
**********************************************************************/
bool
linksToLibrary(const header_info *hi, const char *name)
{
    const struct dylib_command *cmd;
    unsigned long i;
    
    cmd = (const struct dylib_command *) (hi->mhdr() + 1);
    for (i = 0; i < hi->mhdr()->ncmds; i++) {
        if (cmd->cmd == LC_LOAD_DYLIB  ||  cmd->cmd == LC_LOAD_UPWARD_DYLIB  ||
            cmd->cmd == LC_LOAD_WEAK_DYLIB  ||  cmd->cmd == LC_REEXPORT_DYLIB)
        {
            const char *dylib = cmd->dylib.name.offset + (const char *)cmd;
            if (0 == strcmp(dylib, name)) return true;
        }
        cmd = (const struct dylib_command *)((char *)cmd + cmd->cmdsize);
    }

    return false;
}


#if SUPPORT_GC_COMPAT

/***********************************************************************
* shouldRejectGCApp
* Return YES if the executable requires GC.
**********************************************************************/
static bool shouldRejectGCApp(const header_info *hi)
{
    ASSERT(hi->mhdr()->filetype == MH_EXECUTE);

    if (!hi->info()->supportsGC()) {
        // App does not use GC. Don't reject it.
        return NO;
    }
        
    // Exception: Trivial AppleScriptObjC apps can run without GC.
    // 1. executable defines no classes
    // 2. executable references NSBundle only
    // 3. executable links to AppleScriptObjC.framework
    // Note that objc_appRequiresGC() also knows about this.
    size_t classcount = 0;
    size_t refcount = 0;
#if __OBJC2__
    _getObjc2ClassList(hi, &classcount);
    _getObjc2ClassRefs(hi, &refcount);
#else
    if (hi->mod_count == 0  ||  (hi->mod_count == 1 && !hi->mod_ptr[0].symtab)) classcount = 0;
    else classcount = 1;
    _getObjcClassRefs(hi, &refcount);
#endif
    if (classcount == 0  &&  refcount == 1  &&  
        linksToLibrary(hi, "/System/Library/Frameworks"
                       "/AppleScriptObjC.framework/Versions/A"
                       "/AppleScriptObjC"))
    {
        // It's AppleScriptObjC. Don't reject it.
        return NO;
    } 
    else {
        // GC and not trivial AppleScriptObjC. Reject it.
        return YES;
    }
}


/***********************************************************************
* rejectGCImage
* Halt if an image requires GC.
* Testing of the main executable should use rejectGCApp() instead.
**********************************************************************/
static bool shouldRejectGCImage(const headerType *mhdr)
{
    ASSERT(mhdr->filetype != MH_EXECUTE);

    objc_image_info *image_info;
    size_t size;
    
#if !__OBJC2__
    unsigned long seg_size;
    // 32-bit: __OBJC seg but no image_info means no GC support
    if (!getsegmentdata(mhdr, "__OBJC", &seg_size)) {
        // Not objc, therefore not GC. Don't reject it.
        return NO;
    }
    image_info = _getObjcImageInfo(mhdr, &size);
    if (!image_info) {
        // No image_info, therefore not GC. Don't reject it.
        return NO;
    }
#else
    // 64-bit: no image_info means no objc at all
    image_info = _getObjcImageInfo(mhdr, &size);
    if (!image_info) {
        // Not objc, therefore not GC. Don't reject it.
        return NO;
    }
#endif

    return image_info->requiresGC();
}

// SUPPORT_GC_COMPAT
#endif


// Swift currently adds 4 callbacks.
static GlobalSmallVector<objc_func_loadImage, 4> loadImageFuncs;

void objc_addLoadImageFunc(objc_func_loadImage _Nonnull func) {
    // Not supported on the old runtime. Not that the old runtime is supported anyway.
#if __OBJC2__
    mutex_locker_t lock(runtimeLock);
    
    // Call it with all the existing images first.
    for (auto header = FirstHeader; header; header = header->getNext()) {
        func((struct mach_header *)header->mhdr());
    }
    
    // Add it to the vector for future loads.
    loadImageFuncs.append(func);
#endif
}


/***********************************************************************
* map_images_nolock
* Process the given images which are being mapped in by dyld.
* All class registration and fixups are performed (or deferred pending
* discovery of missing superclasses etc), and +load methods are called.
*
* info[] is in bottom-up order i.e. libobjc will be earlier in the 
* array than any library that links to libobjc.
*
* Locking: loadMethodLock(old) or runtimeLock(new) acquired by map_images.
**********************************************************************/
#if __OBJC2__
#include "objc-file.h"
#else
#include "objc-file-old.h"
#endif

/* 映射map
 * 1: 把存储‘共享缓存的地址’存储到dataSegmentsRanges的shared_cache、
      所有hi(不是共享缓存中的库)中所有‘segment的地址‘存到到：dataSegmentsRanges.ranges中(在add数据时，自动扩容)
 * 2: 把‘所有hi’放到全局链表中：FirstHeader
 * 3: 把‘不在共享缓存中所有hi’中所有‘Class.realized状态标记为：NO
 * 4: 初始化：自动释放池（给线程注册自动释放池的销毁函数:tls_dealloc）、SideTableMap（只创建Map）、关联表（只创建Map）、TaggedPointers（设置随机值）
 * 5: 把.cxx_construct、.cxx_destruct、以及所有hi中的sel（既不在共享缓存中的库，也不在经过dyld预优化中的库） 放入全局数组：namedSelectotrs（在此初始化）
 * 6: 把没有优化的hi中的cls添加到：gdb_objc_realized_classes和allocatedClasses中
 * 7: 将所有hi中的protocol放入到全局Map：protocols()中（如果protocol在共享缓存或者dyld必包中有缓存，那么把缓存放到，没有就把自身放入）
 * 8: 有一部分cls是非懒加载的，他们在别线程使用，所以需要realized
     （NSColor、NSApplication、__NSStackBlock__、OS_dispatch_queue、__NSArray0 、Protocol、NSObject等）
 */
void map_images_nolock(unsigned mhCount, const char * const mhPaths[], const struct mach_header * const mhdrs[]) {
    // 静态局部变量：firstTime：共享缓存只允许保存一次
    static bool firstTime = YES;
    
    // 数组：存储多个Image头部
    header_info *hList[mhCount];
    uint32_t hCount;
    size_t selrefCount = 0; // 主App里面sel的个数(__objc_selrefs)和重写了objc的sel个数(__objc_msgrefs, 在我的App的Mach-o文件中没有找到这一模块)例如：retain、release
    
    // 如果有必要的话，会在首次去执行初始化 Perform first-time initialization if necessary.
    // 这个函数在普通的库初始化之前调用 This function is called before ordinary library initializers.
    // 直到一个objc-using的image被找到，才会修正初始化 fixme defer initialization until an objc-using image is found?
    if (firstTime) {        // firstTime，所以只会执行一次
        // 把存储'共享缓存的起始位置'存到：dataSegmentsRanges.shared_cache，并查看是否禁止了共享缓存的预优化开关
        preopt_init();
    }

    if (PrintImages) {
        _objc_inform("IMAGES: processing %u newly-mapped images...\n", mhCount);
    }


    // Find all images with Objective-C metadata.
    hCount = 0;     // 找到所有Image里面的OC的元类对象

    // Count classes. Size various table based on the total.
    int totalClasses = 0;               // 类对象的个数
    int unoptimizedTotalClasses = 0;    // 未优化的类对象个数（不在共享缓存的库里的类）
    {
        // 遍历所有的Image
        uint32_t i = mhCount;
        while (i--) {
            // 获取第i个Image的header
            const headerType *mhdr = (const headerType *)mhdrs[i];

            /* 加载mhdr
             * 1: 校验mhdr是否正确：校验mhdr的heade.magic是否正确
             * 2: mhdr是共享缓存中的库，说明已经完成了加载，可以直接拿到header_info，设置load状态：已加载
             * 3: mhdr不是共享缓存中的库:
             *   3.1: 校验mdhr：只要mhdr中有(__DATA, __objc_imageinfo) 或者 (__OBJC)任何一个都行
             *   3.2: 使用calloc申请内存空间创建header_info
             *   3.3: 初始化header_info：设置mhdr、image_info、load(状态)=true、allClassesRealized(标识位)= NO
             * 4: 统计Image中类个数：
             *   4.1: totalClasses: 所有Image中类的总个数
             *   4.2: unoptimizedTotalClasses:不在共享缓存中Image的类的总个数
             * 5: 把所有的header_info放到全局链表FirstHeader中、
             * 6: 把不在共享缓存的hi所有segment得位置存到到全局数组dataSegmentsRanges.ranges中(在add数据时，自动扩容)
             */
            auto hi = addHeader(mhdr, mhPaths[i], totalClasses, unoptimizedTotalClasses);
            if (!hi) {
                // no objc data in this entry
                continue;
            }
            
            // 如果mhdr是主App
            if (mhdr->filetype == MH_EXECUTE) {
                // Size some data structures based on main executable's size
#if __OBJC2__
                // If dyld3 optimized the main executable, then there shouldn't
                // be any selrefs needed in the dynamic map so we can just init
                // to a 0 sized map
                // hi即不是‘共享缓存’中的库，也没有经过‘必包’优化过的库（我们的App和自定义的依赖库都经过优化，除了部分特殊的库）
                if ( !hi->hasPreoptimizedSelectors() ) {        // App肯定会进入这里
                    // 获取hi文件内的sel指针列表(__objc_selrefs)
                    size_t count;
                    _getObjc2SelectorRefs(hi, &count);
                    // 统计sel的个数
                    selrefCount += count;
                    // 统计hi文件内重写了objc的消息sel个数(__objc_msgrefs, 在我的App的Mach-o文件中没有找到这一模块)，例如：release、retain
                    _getObjc2MessageRefs(hi, &count);
                    selrefCount += count;
                }
#else
                _getObjcSelectorRefs(hi, &selrefCount);
#endif
                
#if SUPPORT_GC_COMPAT       // 0: 真机不走这里
                // Halt if this is a GC app.
                if (shouldRejectGCApp(hi)) {
                    _objc_fatal_with_reason
                        (OBJC_EXIT_REASON_GC_NOT_SUPPORTED, 
                         OS_REASON_FLAG_CONSISTENT_FAILURE, 
                         "Objective-C garbage collection " 
                         "is no longer supported.");
                }
#endif
            }
            
            // 把hi存储到hLizt数组中
            hList[hCount++] = hi;
            
            if (PrintImages) {
                _objc_inform("IMAGES: loading image for %s%s%s%s%s\n", 
                             hi->fname(),
                             mhdr->filetype == MH_BUNDLE ? " (bundle)" : "",
                             hi->info()->isReplacement() ? " (replacement)" : "",
                             hi->info()->hasCategoryClassProperties() ? " (has class properties)" : "",
                             hi->info()->optimizedByDyld()?" (preoptimized)":"");
            }
        }
    }

    // Perform one-time runtime initialization that must be deferred until 
    // the executable itself is found. This needs to be done before 
    // further initialization.
    // (The executable may not be present in this infoList if the 
    // executable does not contain Objective-C code but Objective-C 
    // is dynamically loaded later.
    if (firstTime) {                // firstTime，所以只会执行一次
        // 初始化：全局sel表：namedSelectotrs，并注册.cxx_construct和.cxx_destruct
        // 这里存储的是：Image不是系统共享缓存中的库，也不是经过dyld必包优化过的库
        sel_init(selrefCount);
        // 初始化：自动释放池（给线程注册自动释放池的销毁函数:tls_dealloc）、SideTableMap（只创建Map）、关联表（只创建Map）
        arr_init();

#if SUPPORT_GC_COMPAT       // 0: 真机不走这里
        // Reject any GC images linked to the main executable.
        // We already rejected the app itself above.
        // Images loaded after launch will be rejected by dyld.

        for (uint32_t i = 0; i < hCount; i++) {
            auto hi = hList[i];
            auto mh = hi->mhdr();
            if (mh->filetype != MH_EXECUTE  &&  shouldRejectGCImage(mh)) {
                _objc_fatal_with_reason
                    (OBJC_EXIT_REASON_GC_NOT_SUPPORTED, 
                     OS_REASON_FLAG_CONSISTENT_FAILURE, 
                     "%s requires Objective-C garbage collection "
                     "which is no longer supported.", hi->fname());
            }
        }
#endif

#if TARGET_OS_OSX           // MAC系统，真机不走这里
        // Disable +initialize fork safety if the app is too old (< 10.13).
        // Disable +initialize fork safety if the app has a
        //   __DATA,__objc_fork_ok section.

        if (!dyld_program_sdk_at_least(dyld_platform_version_macOS_10_13)) {
            DisableInitializeForkSafety = true;
            if (PrintInitializing) {
                _objc_inform("INITIALIZE: disabling +initialize fork "
                             "safety enforcement because the app is "
                             "too old.)");
            }
        }

        for (uint32_t i = 0; i < hCount; i++) {
            auto hi = hList[i];
            auto mh = hi->mhdr();
            if (mh->filetype != MH_EXECUTE) continue;
            unsigned long size;
            if (getsectiondata(hi->mhdr(), "__DATA", "__objc_fork_ok", &size)) {
                DisableInitializeForkSafety = true;
                if (PrintInitializing) {
                    _objc_inform("INITIALIZE: disabling +initialize fork "
                                 "safety enforcement because the app has "
                                 "a __DATA,__objc_fork_ok section");
                }
            }
            break;  // assume only one MH_EXECUTE image
        }
#endif

    }

    if (hCount > 0) {       // 我们的App这里肯定 > 0
        /* _read_images
         * 1: 初始化TaggedPointers
         * 2: 将所有hi中的sel（既不在共享缓存中的库，也不在经过dyld预优化的库）放到全局数组：namedSelectors中
         * 3: 把没有优化的hi中的cls添加到gdb_objc_realized_classes和allocatedClasses中
         * 4: 将所有hi中所有的protocol放入到全局Map：protocols()中（如果protocol在共享缓存或者dyld必包中有缓存，那么把缓存放到，没有就把自身放入）
         * 5: 有一部分cls是非懒加载的，他们在别线程使用，所以需要realized
             （NSColor、NSApplication、__NSStackBlock__、OS_dispatch_queue、__NSArray0 、Protocol、NSObject等）
         */
        _read_images(hList, hCount, totalClasses, unoptimizedTotalClasses);
    }

    // 设置标注位为NO，只初始化一次
    firstTime = NO;
    
    // Call image load funcs after everything is set up.
    for (auto func : loadImageFuncs) {      // 断点调试，这个数组是空的
        for (uint32_t i = 0; i < mhCount; i++) {
            func(mhdrs[i]);
        }
    }
}


/***********************************************************************
* unmap_image_nolock
* Process the given image which is about to be unmapped by dyld.
* mh is mach_header instead of headerType because that's what 
*   dyld_priv.h says even for 64-bit.
* 
* Locking: loadMethodLock(both) and runtimeLock(new) acquired by unmap_image.
**********************************************************************/
void 
unmap_image_nolock(const struct mach_header *mh)
{
    if (PrintImages) {
        _objc_inform("IMAGES: processing 1 newly-unmapped image...\n");
    }

    header_info *hi;
    
    // Find the runtime's header_info struct for the image
    for (hi = FirstHeader; hi != NULL; hi = hi->getNext()) {
        if (hi->mhdr() == (const headerType *)mh) {
            break;
        }
    }

    if (!hi) return;

    if (PrintImages) {
        _objc_inform("IMAGES: unloading image for %s%s%s\n", 
                     hi->fname(),
                     hi->mhdr()->filetype == MH_BUNDLE ? " (bundle)" : "",
                     hi->info()->isReplacement() ? " (replacement)" : "");
    }

    _unload_image(hi);

    // Remove header_info from header list
    removeHeader(hi);
    free(hi);
}


/***********************************************************************
* static_init 执行libObjc库(__DATA,__objc_init_func)、(__TEXT, __objc_init_offs)初始化方法
* 备注：执行‘系统级别’C++的静态构造方法，我们自定义的C++静态构造方法在后面由dyld调用
* Run C++ static constructor functions.
* libc调用_objc_init()的时机比dyld调用静态函数早，所以我们要找在_objc_init里调用static_init
* libc calls _objc_init() before dyld would call our static constructors, 
* so we have to do it ourselves.
* 从这里可以看出C++的静态初始化方法存储2个地方
 1: 在__DATA段里，且标记为：__objc_init_func
 2: 在__TEXT段里，且标记为：__objc_init_offs
**********************************************************************/
static void static_init() {
    size_t count;
    // 获取libObjc库里__DATA段里存储的所有初始化方法__objc_init_func
    auto inits = getLibobjcInitializers(&_mh_dylib_header, &count);
    // 调用这些初始化方法
    for (size_t i = 0; i < count; i++) {
        inits[i]();
    }
    // 获取libObjc库里__TEXT段里存储的所有初始化方法__objc_init_offs
    auto offsets = getLibobjcInitializerOffsets(&_mh_dylib_header, &count);
    // 调用这些初始化方法
    for (size_t i = 0; i < count; i++) {
        UnsignedInitializer init(offsets[i]);
        init();
    }
}


/***********************************************************************
* _objc_atfork_prepare
* _objc_atfork_parent
* _objc_atfork_child
* Allow ObjC to be used between fork() and exec().
* libc requires this because it has fork-safe functions that use os_objects.
*
* _objc_atfork_prepare() acquires all locks.
* _objc_atfork_parent() releases the locks again.
* _objc_atfork_child() forcibly resets the locks.
**********************************************************************/

// Declare lock ordering.
#if LOCKDEBUG
__attribute__((constructor))
static void defineLockOrder()
{
    // Every lock precedes crashlog_lock
    // on the assumption that fatal errors could be anywhere.
    lockdebug_lock_precedes_lock(&loadMethodLock, &crashlog_lock);
    lockdebug_lock_precedes_lock(&classInitLock, &crashlog_lock);
#if __OBJC2__
    lockdebug_lock_precedes_lock(&runtimeLock, &crashlog_lock);
    lockdebug_lock_precedes_lock(&DemangleCacheLock, &crashlog_lock);
#else
    lockdebug_lock_precedes_lock(&classLock, &crashlog_lock);
    lockdebug_lock_precedes_lock(&methodListLock, &crashlog_lock);
    lockdebug_lock_precedes_lock(&NXUniqueStringLock, &crashlog_lock);
    lockdebug_lock_precedes_lock(&impLock, &crashlog_lock);
#endif
    lockdebug_lock_precedes_lock(&selLock, &crashlog_lock);
#if CONFIG_USE_CACHE_LOCK
    lockdebug_lock_precedes_lock(&cacheUpdateLock, &crashlog_lock);
#endif
    lockdebug_lock_precedes_lock(&objcMsgLogLock, &crashlog_lock);
    lockdebug_lock_precedes_lock(&AltHandlerDebugLock, &crashlog_lock);
    lockdebug_lock_precedes_lock(&AssociationsManagerLock, &crashlog_lock);
    SideTableLocksPrecedeLock(&crashlog_lock);
    PropertyLocks.precedeLock(&crashlog_lock);
    StructLocks.precedeLock(&crashlog_lock);
    CppObjectLocks.precedeLock(&crashlog_lock);

    // loadMethodLock precedes everything
    // because it is held while +load methods run
    lockdebug_lock_precedes_lock(&loadMethodLock, &classInitLock);
#if __OBJC2__
    lockdebug_lock_precedes_lock(&loadMethodLock, &runtimeLock);
    lockdebug_lock_precedes_lock(&loadMethodLock, &DemangleCacheLock);
#else
    lockdebug_lock_precedes_lock(&loadMethodLock, &methodListLock);
    lockdebug_lock_precedes_lock(&loadMethodLock, &classLock);
    lockdebug_lock_precedes_lock(&loadMethodLock, &NXUniqueStringLock);
    lockdebug_lock_precedes_lock(&loadMethodLock, &impLock);
#endif
    lockdebug_lock_precedes_lock(&loadMethodLock, &selLock);
#if CONFIG_USE_CACHE_LOCK
    lockdebug_lock_precedes_lock(&loadMethodLock, &cacheUpdateLock);
#endif
    lockdebug_lock_precedes_lock(&loadMethodLock, &objcMsgLogLock);
    lockdebug_lock_precedes_lock(&loadMethodLock, &AltHandlerDebugLock);
    lockdebug_lock_precedes_lock(&loadMethodLock, &AssociationsManagerLock);
    SideTableLocksSucceedLock(&loadMethodLock);
    PropertyLocks.succeedLock(&loadMethodLock);
    StructLocks.succeedLock(&loadMethodLock);
    CppObjectLocks.succeedLock(&loadMethodLock);

    // PropertyLocks and CppObjectLocks and AssociationManagerLock 
    // precede everything because they are held while objc_retain() 
    // or C++ copy are called.
    // (StructLocks do not precede everything because it calls memmove only.)
    auto PropertyAndCppObjectAndAssocLocksPrecedeLock = [&](const void *lock) {
        PropertyLocks.precedeLock(lock);
        CppObjectLocks.precedeLock(lock);
        lockdebug_lock_precedes_lock(&AssociationsManagerLock, lock);
    };
#if __OBJC2__
    PropertyAndCppObjectAndAssocLocksPrecedeLock(&runtimeLock);
    PropertyAndCppObjectAndAssocLocksPrecedeLock(&DemangleCacheLock);
#else
    PropertyAndCppObjectAndAssocLocksPrecedeLock(&methodListLock);
    PropertyAndCppObjectAndAssocLocksPrecedeLock(&classLock);
    PropertyAndCppObjectAndAssocLocksPrecedeLock(&NXUniqueStringLock);
    PropertyAndCppObjectAndAssocLocksPrecedeLock(&impLock);
#endif
    PropertyAndCppObjectAndAssocLocksPrecedeLock(&classInitLock);
    PropertyAndCppObjectAndAssocLocksPrecedeLock(&selLock);
#if CONFIG_USE_CACHE_LOCK
    PropertyAndCppObjectAndAssocLocksPrecedeLock(&cacheUpdateLock);
#endif
    PropertyAndCppObjectAndAssocLocksPrecedeLock(&objcMsgLogLock);
    PropertyAndCppObjectAndAssocLocksPrecedeLock(&AltHandlerDebugLock);

    SideTableLocksSucceedLocks(PropertyLocks);
    SideTableLocksSucceedLocks(CppObjectLocks);
    SideTableLocksSucceedLock(&AssociationsManagerLock);

    PropertyLocks.precedeLock(&AssociationsManagerLock);
    CppObjectLocks.precedeLock(&AssociationsManagerLock);
    
#if __OBJC2__
    lockdebug_lock_precedes_lock(&classInitLock, &runtimeLock);
#endif

#if __OBJC2__
    // Runtime operations may occur inside SideTable locks
    // (such as storeWeak calling getMethodImplementation)
    SideTableLocksPrecedeLock(&runtimeLock);
    SideTableLocksPrecedeLock(&classInitLock);
    // Some operations may occur inside runtimeLock.
    lockdebug_lock_precedes_lock(&runtimeLock, &selLock);
#if CONFIG_USE_CACHE_LOCK
    lockdebug_lock_precedes_lock(&runtimeLock, &cacheUpdateLock);
#endif
    lockdebug_lock_precedes_lock(&runtimeLock, &DemangleCacheLock);
#else
    // Runtime operations may occur inside SideTable locks
    // (such as storeWeak calling getMethodImplementation)
    SideTableLocksPrecedeLock(&methodListLock);
    SideTableLocksPrecedeLock(&classInitLock);
    // Method lookup and fixup.
    lockdebug_lock_precedes_lock(&methodListLock, &classLock);
    lockdebug_lock_precedes_lock(&methodListLock, &selLock);
#if CONFIG_USE_CACHE_LOCK
    lockdebug_lock_precedes_lock(&methodListLock, &cacheUpdateLock);
#endif
    lockdebug_lock_precedes_lock(&methodListLock, &impLock);
    lockdebug_lock_precedes_lock(&classLock, &selLock);
    lockdebug_lock_precedes_lock(&classLock, &cacheUpdateLock);
#endif

    // Striped locks use address order internally.
    SideTableDefineLockOrder();
    PropertyLocks.defineLockOrder();
    StructLocks.defineLockOrder();
    CppObjectLocks.defineLockOrder();
}
// LOCKDEBUG
#endif

static bool ForkIsMultithreaded;
void _objc_atfork_prepare()
{
    // Save threaded-ness for the child's use.
    ForkIsMultithreaded = pthread_is_threaded_np();

    lockdebug_assert_no_locks_locked();
    lockdebug_setInForkPrepare(true);

    loadMethodLock.lock();
    PropertyLocks.lockAll();
    CppObjectLocks.lockAll();
    AssociationsManagerLock.lock();
    SideTableLockAll();
    classInitLock.enter();
#if __OBJC2__
    runtimeLock.lock();
    DemangleCacheLock.lock();
#else
    methodListLock.lock();
    classLock.lock();
    NXUniqueStringLock.lock();
    impLock.lock();
#endif
    selLock.lock();
#if CONFIG_USE_CACHE_LOCK
    cacheUpdateLock.lock();
#endif
    objcMsgLogLock.lock();
    AltHandlerDebugLock.lock();
    StructLocks.lockAll();
    crashlog_lock.lock();

    lockdebug_assert_all_locks_locked();
    lockdebug_setInForkPrepare(false);
}

void _objc_atfork_parent()
{
    lockdebug_assert_all_locks_locked();

    CppObjectLocks.unlockAll();
    StructLocks.unlockAll();
    PropertyLocks.unlockAll();
    AssociationsManagerLock.unlock();
    AltHandlerDebugLock.unlock();
    objcMsgLogLock.unlock();
    crashlog_lock.unlock();
    loadMethodLock.unlock();
#if CONFIG_USE_CACHE_LOCK
    cacheUpdateLock.unlock();
#endif
    selLock.unlock();
    SideTableUnlockAll();
#if __OBJC2__
    DemangleCacheLock.unlock();
    runtimeLock.unlock();
#else
    impLock.unlock();
    NXUniqueStringLock.unlock();
    methodListLock.unlock();
    classLock.unlock();
#endif
    classInitLock.leave();

    lockdebug_assert_no_locks_locked();
}

void _objc_atfork_child()
{
    // Turn on +initialize fork safety enforcement if applicable.
    if (ForkIsMultithreaded  &&  !DisableInitializeForkSafety) {
        MultithreadedForkChild = true;
    }

    lockdebug_assert_all_locks_locked();

    CppObjectLocks.forceResetAll();
    StructLocks.forceResetAll();
    PropertyLocks.forceResetAll();
    AssociationsManagerLock.forceReset();
    AltHandlerDebugLock.forceReset();
    objcMsgLogLock.forceReset();
    crashlog_lock.forceReset();
    loadMethodLock.forceReset();
#if CONFIG_USE_CACHE_LOCK
    cacheUpdateLock.forceReset();
#endif
    selLock.forceReset();
    SideTableForceResetAll();
#if __OBJC2__
    DemangleCacheLock.forceReset();
    runtimeLock.forceReset();
#else
    impLock.forceReset();
    NXUniqueStringLock.forceReset();
    methodListLock.forceReset();
    classLock.forceReset();
#endif
    classInitLock.forceReset();

    lockdebug_assert_no_locks_locked();
}


/***********************************************************************
* _objc_init  runtime的初始化函数
* Bootstrap initialization. Registers our image notifier with dyld.
* Called by libSystem BEFORE library initialization time
**********************************************************************/
void _objc_init(void) {
    static bool initialized = false;        // 静态局部变量，标记是否已完成初始化
    if (initialized) return;    // 已做过初始化，不再做
    initialized = true;
    
    // fixme defer initialization until an objc-using image is found?
    // 环境变量初始化：
    // ● 读取我们给项目设置的环境变量；设置OBJC_HELP可以打印出支持的所有环境变量和对应的说明。
    // ● 读取其他工程配置：NSZombiesEnabled僵尸对象检测
    environ_init();
    // 通过线程key绑定的方式注册的线程销毁函数：key=_objc_pthread_data, value=_objc_pthread_destroyspecific
    // _objc_pthread_destroyspecific: 释放_objc_pthread_data数据：initializingClasses(初始化类对象)、syncCache(synchronize锁)、handlerList(exception属性)、classNameLookups(objc_getClass() hooks)、释放_objc_pthread_data
    tls_init();
    // 执行libObjc库的的C++的静态构造方法：
    // 1: 在__DATA段里，且标记为__objc_init_func
    // 2: 在__TEXT段里，且标记为__objc_init_offs
    // objc_init后面需要进行一些初始化，所以这里libcObjc先主动调用了系统库的C++静态构造函数，
    // 如果我们在库中实现了自定义C++静态构造函数，那么是由dyld在初始化image的时候调用的
    // 所以系统级别的C++构造函数 优先于 自定义的C++构造函数
    static_init();
    // 1: 初始化全局Map: unattachedCategories  -> [key(类对象) : value(类对象的扩展数组)]
    // 2: 初始化objc命名空间下的全局Set: allocatedClasses  -> 存储的是通过函数：objc_allocateClassPair 创建的类对象，也就是运行时创建的类对象
    runtime_init();
    // 初始化libobjc的异常系统，设置异常函数_objc_terminate
    /* _objc_terminate
     * 1. 检查是否是一个活跃的异常
     * 2. 如果是活跃的异常，检查是否是OC抛出的异常
     * 3. 如果是OC抛出的异常，调用uncaught_handler回调函数指针
     * 4. 不是OC抛出异常，继续C++终止操作
     */
    exception_init();
#if __OBJC2__
    // 实现在：objc-cache的1126行，手机端这个函数什么都没做
    cache_t::init();
#endif
    // 手机端这里什么也没做
    _imp_implementationWithBlock_init();

    /*map_images
     * 1: 把存储‘共享缓存的起始位置’存储到dataSegmentsRanges.shared_cache、
          所有hi(不是共享缓存中的库)中所有‘segment的地址‘存到到：dataSegmentsRanges.ranges中(在add数据时，自动扩容)
     * 2: 把‘所有hi’放到全局链表中：FirstHeader
     * 3: 把‘不在共享缓存中所有hi’中所有‘Class.realized状态标记为：NO
     * 4: 初始化：自动释放池（给线程注册自动释放池的销毁函数:tls_dealloc）、SideTableMap（只创建Map）、关联表（只创建Map）、TaggedPointers（设置随机值）
     * 5: 把.cxx_construct、.cxx_destruct、以及所有hi中的sel 放入全局数组：namedSelectotrs
     * 6: 把没有优化的hi中的cls添加到：gdb_objc_realized_classes和allocatedClasses中
     * 7: 将所有hi中的protocol放入到全局Map：protocols()中（如果protocol在共享缓存或者dyld必包中有缓存，那么把缓存放到，没有就把自身放入）
     * 8: 有一部分cls是非懒加载的，他们在别线程使用，所以需要realized
         （NSColor、NSApplication、__NSStackBlock__、OS_dispatch_queue、__NSArray0 、Protocol、NSObject等）
     */
    /* load_images的内部执行：
     * 1: 把mach-o文件中所有的分类数据cat都加载都对应的类对象cls中
     *  1.1 cls完成了realized，存储到rwe中
     *  1.2 cls未完成realized，存储到全局变量unattachedCategories中
     * 2: 把mach-o中实现了load方法的类对象cls添加到全局数组：loadable_classes 中
     *  2.1 会递归父类，如果父类实现了load，那么父类在子类的前面
     * 3: 把mach-o中实现了load方法的cat添加到全局数组：loadable_categories 中
     * 4: 让数组loadable_classes所有实现了load方法的类，都执行load方法（直接执行，没有走obj_msgSend），最后释放这个数组
     * 5: 让数组loadable_categories中的所有的cat，都执行load方法，最后释放这个数组
     */
    _dyld_objc_notify_register(&map_images, load_images, unmap_image);

#if __OBJC2__
    didCallDyldNotifyRegister = true;
#endif
}


/***********************************************************************
* _headerForAddress.
* addr can be a class or a category
**********************************************************************/
static const header_info *_headerForAddress(void *addr)
{
#if __OBJC2__
    const char *segnames[] = { "__DATA", "__DATA_CONST", "__DATA_DIRTY" };
#else
    const char *segnames[] = { "__OBJC" };
#endif
    header_info *hi;

    for (hi = FirstHeader; hi != NULL; hi = hi->getNext()) {
        for (size_t i = 0; i < sizeof(segnames)/sizeof(segnames[0]); i++) {
            unsigned long seg_size;            
            uint8_t *seg = getsegmentdata(hi->mhdr(), segnames[i], &seg_size);
            if (!seg) continue;
            
            // Is the class in this header?
            if ((uint8_t *)addr >= seg  &&  (uint8_t *)addr < seg + seg_size) {
                return hi;
            }
        }
    }

    // Not found
    return 0;
}


/***********************************************************************
* _headerForClass
* Return the image header containing this class, or NULL.
* Returns NULL on runtime-constructed classes, and the NSCF classes.
**********************************************************************/
const header_info *_headerForClass(Class cls)
{
    return _headerForAddress(cls);
}


/**********************************************************************
* secure_open
* Securely open a file from a world-writable directory (like /tmp)
* If the file does not exist, it will be atomically created with mode 0600
* If the file exists, it must be, and remain after opening: 
*   1. a regular file (in particular, not a symlink)
*   2. owned by euid
*   3. permissions 0600
*   4. link count == 1
* Returns a file descriptor or -1. Errno may or may not be set on error.
**********************************************************************/
int secure_open(const char *filename, int flags, uid_t euid)
{
    struct stat fs, ls;
    int fd = -1;
    bool truncate = NO;
    bool create = NO;

    if (flags & O_TRUNC) {
        // Don't truncate the file until after it is open and verified.
        truncate = YES;
        flags &= ~O_TRUNC;
    }
    if (flags & O_CREAT) {
        // Don't create except when we're ready for it
        create = YES;
        flags &= ~O_CREAT;
        flags &= ~O_EXCL;
    }

    if (lstat(filename, &ls) < 0) {
        if (errno == ENOENT  &&  create) {
            // No such file - create it
            fd = open(filename, flags | O_CREAT | O_EXCL, 0600);
            if (fd >= 0) {
                // File was created successfully.
                // New file does not need to be truncated.
                return fd;
            } else {
                // File creation failed.
                return -1;
            }
        } else {
            // lstat failed, or user doesn't want to create the file
            return -1;
        }
    } else {
        // lstat succeeded - verify attributes and open
        if (S_ISREG(ls.st_mode)  &&  // regular file?
            ls.st_nlink == 1  &&     // link count == 1?
            ls.st_uid == euid  &&    // owned by euid?
            (ls.st_mode & ALLPERMS) == (S_IRUSR | S_IWUSR))  // mode 0600?
        {
            // Attributes look ok - open it and check attributes again
            fd = open(filename, flags, 0000);
            if (fd >= 0) {
                // File is open - double-check attributes
                if (0 == fstat(fd, &fs)  &&  
                    fs.st_nlink == ls.st_nlink  &&  // link count == 1?
                    fs.st_uid == ls.st_uid  &&      // owned by euid?
                    fs.st_mode == ls.st_mode  &&    // regular file, 0600?
                    fs.st_ino == ls.st_ino  &&      // same inode as before?
                    fs.st_dev == ls.st_dev)         // same device as before?
                {
                    // File is open and OK
                    if (truncate) ftruncate(fd, 0);
                    return fd;
                } else {
                    // Opened file looks funny - close it
                    close(fd);
                    return -1;
                }
            } else {
                // File didn't open
                return -1;
            }
        } else {
            // Unopened file looks funny - don't open it
            return -1;
        }
    }
}


#if TARGET_OS_IPHONE

const char *__crashreporter_info__ = NULL;

const char *CRSetCrashLogMessage(const char *msg)
{
    __crashreporter_info__ = msg;
    return msg;
}
const char *CRGetCrashLogMessage(void)
{
    return __crashreporter_info__;
}

#endif

// TARGET_OS_MAC
#else


#error unknown OS


#endif
